<!doctype html>
<!--
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
  <meta charset="utf-8">
  <script src="../../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
  <script src="wct-browser-config.js"></script>
  <script src="../../node_modules/wct-browser-legacy/browser.js"></script>
  <script type="module" src="../../polymer-legacy.js"></script>
  <script type="module" src="./attributes-elements.js"></script>
<body>

  <test-fixture id="basic">
    <template>
      <x-basic attr3="instance"></x-basic>
    </template>
  </test-fixture>

  <test-fixture id="compose">
    <template>
      <x-compose></x-compose>
    </template>
  </test-fixture>

  <test-fixture id="basic-with-attributes">
    <template>
      <x-basic
        object='{"foo": "bar", "nested": {"meaning": 42}, "arr": [0, "foo", true]}'
        array='[0, "foo", true, {"foo": "bar"}]'
        number="42"
        string="The quick brown fox"
        bool
        date="Wed Mar 04 2015 10:46:05 GMT-0800 (PST)"
        -u-p-c-a-s-e="The quick brown fox"
        dash-case="The quick brown fox"
        no-type="Should be String"
        read-only="Should not change"
        class="Should not deserialize"
        nard="Should not deserialize"
        behavior-bool
        behavior-number="77"
        shorthand-bool
        shorthand-number="88"
        behavior-shorthand-bool
        behavior-shorthand-number="99"
        foreign-type-object-prop="The quick brown fox">
      </x-basic>
    </template>
  </test-fixture>

  <test-fixture id="reflect-to-attribute">
    <template>
      <x-reflect></x-reflect>
    </template>
  </test-fixture>

  <test-fixture id="reflect-to-attribute-and-attributes">
    <template>
      <x-reflect
        object='{"foo": "bar", "nested": {"meaning": 42}, "arr": [0, "foo", true]}'
        array='[0, "foo", true, {"foo": "bar"}]'
        number="42"
        string="The quick brown fox"
        bool
        date="Wed Mar 04 2015 10:46:05 GMT-0800 (PST)"
        dash-case="The quick brown fox"
        -u-p-c-a-s-e="The quick brown fox"
        no-type="Should be String"
        read-only="Should not change"
        class="Should not deserialize"
        nard="Should not deserialize"
        behavior-bool
        behavior-number="77"
        shorthand-bool
        shorthand-number="88"
        behavior-shorthand-bool
        behavior-shorthand-number="99"
        foreign-type-object-prop="The quick brown fox">
      </x-reflect>
    </template>
  </test-fixture>

  <script type="module">
import './attributes-elements.js';
suite('create-time deserialization', function() {
  function testAttributesMatchDefaults(element) {
    assert.deepEqual(element.object, {});
    assert.deepEqual(element.array, []);
    assert.strictEqual(element.number, 0);
    assert.strictEqual(element.string, 'none');
    assert.strictEqual(element.bool, true);
    assert.strictEqual(element.negBool, false);
    assert.strictEqual(element.date.getTime(), 0);
    assert.strictEqual(element.dashCase, 'none');
    assert.strictEqual(element.UPCASE, 'none');
    assert.strictEqual(element.noType, 'none');
    assert.strictEqual(element.readOnly, 'default');
    assert.strictEqual(element.behaviorNumber, 11);
    assert.strictEqual(element.behaviorBool, false);
    assert.strictEqual(element.shorthandNumber, undefined);
    assert.strictEqual(element.shorthandBool, undefined);
    assert.strictEqual(element.behaviorShorthandNumber, undefined);
    assert.strictEqual(element.behaviorShorthandBool, undefined);
    assert.strictEqual(element.foreignTypeObjectProp, 'none');
  }

  function testAttributesMatchCustomConfiguration(element) {
    var configuredObject = {foo: 'bar', nested: {'meaning': 42}, arr: [0, 'foo', true]};
    var configuredArray = [0, 'foo', true, {foo: 'bar'}];
    var configuredString = "The quick brown fox";
    var configuredTime = 1425494765000;

    assert.deepEqual(element.object, configuredObject);
    assert.deepEqual(element.array, configuredArray);
    assert.strictEqual(element.number, 42);
    assert.strictEqual(element.string, 'The quick brown fox');
    assert.strictEqual(element.bool, true);
    assert.strictEqual(element.negBool, false);
    assert.strictEqual(element.date.getTime(), configuredTime);
    assert.strictEqual(element.dashCase, configuredString);
    assert.strictEqual(element.UPCASE, configuredString);
    assert.strictEqual(element.noType, 'Should be String');
    assert.strictEqual(element.readOnly, 'default');
    assert.strictEqual(element.class, undefined);
    assert.strictEqual(element.nard, undefined);
    assert.strictEqual(element.behaviorNumber, 77);
    assert.strictEqual(element.behaviorBool, true);
    assert.strictEqual(element.shorthandNumber, 88);
    assert.strictEqual(element.shorthandBool, true);
    assert.strictEqual(element.behaviorShorthandNumber, 99);
    assert.strictEqual(element.behaviorShorthandBool, true);
    assert.strictEqual(element.foreignTypeObjectProp, configuredString);
  }

  test('basic default values', function() {
    var element = fixture('basic');
    testAttributesMatchDefaults(element);
  });

  test('basic deserialize attributes', function() {
    var element = fixture('basic-with-attributes');
    testAttributesMatchCustomConfiguration(element);
  });

  test('reflected default values', function() {
    var element = fixture('reflect-to-attribute');
    testAttributesMatchDefaults(element);
  });

  test('reflected deserialize attributes', function() {
    var element = fixture('reflect-to-attribute-and-attributes');
    testAttributesMatchCustomConfiguration(element);
  });

  test('reflected warned about reflection for UPCASE', function() {
    var element = fixture('reflect-to-attribute');
    assert.isTrue(element.__warnedAboutUPCASE = true);
  });
});

suite('imperative attribute change (no-reflect)', function() {
  var element, configuredObject, configuredArray;

  setup(function() {
    element = fixture('basic');
    configuredObject = {foo: 'bar', nested: {'meaning': 42}, arr: [0, 'foo', true]};
    configuredArray = [0, 'foo', true, {foo: 'bar'}];
  });

  test('change object attribute', function() {
    element.setAttribute('object', '{"foo": "bar", "nested": {"meaning": 42}, "arr": [0, "foo", true]}');
    assert.deepEqual(element.object, configuredObject);
  });

  test('change object attribute to foreign type', function() {
    element.setAttribute('object', 'The quick brown fox');
    assert.strictEqual(element.object, 'The quick brown fox');
  });

  test('change array attribute', function() {
    element.setAttribute('array', '[0, "foo", true, {"foo": "bar"}]');
    assert.deepEqual(element.array, configuredArray);
  });

  test('change number attribute', function() {
    element.setAttribute('number', 42);
    assert.strictEqual(element.number, 42);
  });

  test('change string attribute', function() {
    element.setAttribute('string', 'howdy');
    assert.strictEqual(element.string, 'howdy');
  });

  test('change boolean attribute true', function() {
    element.setAttribute('bool', '');
    assert.strictEqual(element.bool, true);
  });

  test('change boolean attribute truthy', function() {
    element.setAttribute('bool', 'sure!');
    assert.strictEqual(element.bool, true);
  });

  test('change boolean attribute false', function() {
    element.setAttribute('bool', '');
    assert.strictEqual(element.bool, true);
    element.removeAttribute('bool');
    assert.strictEqual(element.bool, false);
  });

  test('change dashCase attribute', function() {
    element.setAttribute('dash-case', 'Changed');
    assert.strictEqual(element.dashCase, 'Changed');
  });

  test('value that cannot deserialize from JSON warns', function() {
    sinon.spy(console, 'warn');
    var badAttr = '[foo, bar]';
    element.setAttribute('array', badAttr);
    assert.isTrue(console.warn.calledOnce);
    assert.include(console.warn.firstCall.args[0], badAttr);
    console.warn.restore();
  });

});

suite('imperative attribute change (reflect)', function() {
  var element, configuredObject, configuredArray;

  setup(function() {
    element = fixture('reflect-to-attribute');
    configuredObject = {foo: 'bar', nested: {'meaning': 42}, arr: [0, 'foo', true]};
    configuredArray = [0, 'foo', true, {foo: 'bar'}];
  });

  test('change object attribute', function() {
    element.setAttribute('object', '{"foo": "bar", "nested": {"meaning": 42}, "arr": [0, "foo", true]}');
    assert.deepEqual(element.object, configuredObject);
  });

  test('change object attribute to foreign type', function() {
    element.setAttribute('object', 'The quick brown fox');
    assert.strictEqual(element.object, 'The quick brown fox');
  });

  test('change array attribute', function() {
    element.setAttribute('array', '[0, "foo", true, {"foo": "bar"}]');
    assert.deepEqual(element.array, configuredArray);
  });

  test('change number attribute', function() {
    element.setAttribute('number', 42);
    assert.strictEqual(element.number, 42);
  });

  test('change string attribute', function() {
    element.setAttribute('string', 'howdy');
    assert.strictEqual(element.string, 'howdy');
  });

  test('change boolean attribute true', function() {
    element.setAttribute('bool', '');
    assert.strictEqual(element.bool, true);
  });

  test('change boolean attribute truthy', function() {
    element.setAttribute('bool', 'sure!');
    assert.strictEqual(element.bool, true);
  });

  test('change boolean attribute false', function() {
    element.setAttribute('bool', '');
    assert.strictEqual(element.bool, true);
    element.removeAttribute('bool');
    assert.strictEqual(element.bool, false);
  });

  test('change dashCase attribute', function() {
    element.setAttribute('dash-case', 'Changed');
    assert.strictEqual(element.dashCase, 'Changed');
  });

  test('change non-`properties` property that natively reflects', function() {
    element.title = 'awesome';
    assert.strictEqual(element.title, 'awesome');
    element.title = '';
    assert.strictEqual(element.title, '');
    element.setAttribute('title', 'super');
    assert.strictEqual(element.title, 'super');
    element.removeAttribute('title');
    assert.strictEqual(element.title, '');
  });

});

suite('hostAttributes', function() {
  var configuredObject;

  setup(function() {
    configuredObject = {foo: 'bar', nested: {'meaning': 42}, arr: [0, 'foo', true]};
  });

  test('hostAttributes set correctly', function() {
    var element = fixture('basic');
    assert.strictEqual(element.getAttribute('attr1'), 'this is attr 1');
    assert.strictEqual(element.getAttribute('attr2'), '42');
    assert.strictEqual(element.getAttribute('attr3'), 'instance', 'host attribute overrode instance attribute and should not');
    assert.strictEqual(element.getAttribute('aria-role'), 'button');
    assert.strictEqual(element.getAttribute('title'), 'awesome');
    assert.strictEqual(element.title, 'awesome');
    assert.equal(element.getAttribute('attr-object'), JSON.stringify(configuredObject));
    assert.equal(element.hasAttribute('attr-stupid'), false);
    // class *is* serialized
    // BREAKME(sorvell): document breaking change.
    assert.ok(element.classList.contains('foo'));
    assert.ok(element.classList.contains('bar'));
    assert.ok(element.classList.contains('baz'));

  });

  test('hostAttributes set correctly in composed element', function() {
    var element = fixture('compose');
    assert.strictEqual(element.$.basic.getAttribute('attr1'), 'compose');
    assert.strictEqual(element.$.basic.getAttribute('attr2'), '42');
    assert.strictEqual(element.$.basic.getAttribute('aria-role'), 'button');
    assert.strictEqual(element.$.basic.getAttribute('title'), 'awesome');
    assert.strictEqual(element.$.basic.title, 'awesome');

    assert.equal(element.$.basic.getAttribute('attr-object'), JSON.stringify(configuredObject));
    assert.equal(element.$.basic.hasAttribute('attr-stupid'), false);

    // class does not overwrite user setting
    assert.notOk(element.$.basic.classList.contains('foo'));
    assert.notOk(element.$.basic.classList.contains('bar'));
    assert.notOk(element.$.basic.classList.contains('baz'));

    assert(element.$.basic.classList.contains('should-not-override'));

    // applied to property with effect
    assert.strictEqual(element.$.basic.prop, 'compose');
    assert.equal(element.$.basic.propChangedCount, 1);
    assert.equal(element.$.basic.attr1ChangedCount, 1);
    assert.equal(element.prop2, 'hi');
  });

});
</script>
</body>
</html>
